/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package BusinessLogic;

import Entity.*;
import EntityState.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.apache.commons.codec.digest.DigestUtils;

/**
 *
 * @author James
 */
@Stateless
public class TCubeBean implements TCubeBeanLocal {
    //Resources
    @Resource(mappedName = "jms/QueueConnectionFactory")
    private ConnectionFactory queueConnectionFactory;
    @Resource(mappedName = "jms/OutgoingMailQueue")
    private Queue emailQueue;
    @PersistenceContext(unitName = "TCube-ejbPU")
    private EntityManager em;
    
    @Override
    public int checkAvailability(String username) {
        if (em.find(TCubeAccount.class, username) == null) return Constants.STATUS_SUCCESSFUL;
        else return Constants.STATUS_FAILED;
    }

    @Override
    public int registerAccount(String username, String password, String IC, String firstName, String lastName, String gender, String countryCode, String phone, String email, String country, String city, int accountLevel, int theRole) {
        //Check if the database contains user with same NRIC or email
        Query q = em.createQuery("SELECT T FROM TCubeAccount T WHERE T.IC=:IC OR T.email=:email OR T.username=:username");
        q.setParameter("IC", IC);
        q.setParameter("email", email);
        q.setParameter("username", username);

        //If this user is unique, create an entry
        if (!q.getResultList().isEmpty()) return Constants.STATUS_NOT_UNIQUE;
        else {
            TCubeRole    tr = em.find(TCubeRole.class, theRole);
            TCubeAccount ta = new TCubeAccount();
            List<TCubePrivilege> listOfPrivilege = ta.getListOfPrivileges();
            for (TCubePrivilege tp : tr.getListOfPrivileges()) listOfPrivilege.add(tp);
            ta.createTCubeAccount(username, DigestUtils.sha256Hex(password), IC, firstName, lastName, gender, countryCode, phone, email, country, city, accountLevel);
            String id = getUniqueID();
            while (em.find(InactivatedTCubeAccount.class, id) != null) id = getUniqueID(); 
            InactivatedTCubeAccount iu = new InactivatedTCubeAccount(id, ta);
            em.persist(iu);
            em.flush();
            String link = "http://localhost:8080/portal/internal/TCubeAccountActivation?id=" + id; 
            sendEmail(Constants.MAIL_ACTIVATION, email, link);
            return Constants.STATUS_SUCCESSFUL;
        }
    }

    @Override
    public int activateUser(String id) {
        InactivatedTCubeAccount ita = em.find(InactivatedTCubeAccount.class, id);
        if (ita == null) return Constants.STATUS_NOT_FOUND;
        else {
            ita.getAccount().setActivated(true);
            em.remove(ita);
            return Constants.STATUS_SUCCESSFUL;
        }
    }

    @Override
    public TCubeAccountState login(String username, String password, String timezone) {
        TCubeAccount ta = em.find(TCubeAccount.class, username);
        if (ta == null || !ta.getPassword().equals(DigestUtils.sha256Hex(password))) return null;
        else {
            TCubeAccountState tas;
            if (ta.isActive() && ta.isActivated()) {
                if (!ta.getTimezone().equals(timezone)) ta.setTimezone(timezone); //Update timezone everytime a user log in
                
                //Create the State objects
                List<TCubePrivilegeState> listOfPrivileges = new ArrayList<TCubePrivilegeState>();
                for (TCubePrivilege tp : ta.getListOfPrivileges()) {
                    TCubePrivilegeState tps = new TCubePrivilegeState(tp.getId(), tp.getName(), tp.getDescription(), tp.getCreatedDate());
                    listOfPrivileges.add(tps);
                }
                tas = new TCubeAccountState(ta.getIC(), ta.getFirstName(), ta.getLastName(), ta.getGender(), ta.getTimezone(), ta.getAddress1(), ta.getAddress2(), ta.getPostalCode(), ta.getCountryCode(), ta.getPhone(), ta.getEmail(), ta.getCountry(), ta.getCity(), username, ta.isActive(), ta.isActivated(), ta.getAccountLevel(), ta.getBirthday(), ta.getRegisteredDate(), listOfPrivileges);
            } else tas = new TCubeAccountState(ta.isActive(), ta.isActivated());
            return tas;
        }
    }
    
    @Override
    public boolean checkAccountLevel(String username, int requiredAccountLevel) {
        TCubeAccount ta = em.find(TCubeAccount.class, username);
        if (ta.getAccountLevel() >= requiredAccountLevel) return true;
        else return false;
    }
    
    @Override //TODO: change to more efficient method, get a full list of privilege in servlet and check from user cookie
    public boolean checkPrivilege(String username, int requiredPrivilege) {
        TCubeAccount ta = em.find(TCubeAccount.class, username);
        if (ta.getAccountLevel() == Constants.ACCOUNT_LEVEL_ROOT) return true; //root level account
        else {
            for (TCubePrivilege tp : ta.getListOfPrivileges()) {
                if (tp.getId() == requiredPrivilege) return true;
            }
            return false;
        }
    }
    
    @Override
    public boolean checkAccountLevel(String username1, String username2) {
        TCubeAccount ta1 = em.find(TCubeAccount.class, username1);
        TCubeAccount ta2 = em.find(TCubeAccount.class, username2);
        return ta1.getAccountLevel() >= ta2.getAccountLevel();
    }
    
    @Override
    public List<TCubeRolesState> getFullListOfRoles() {
        Query q = em.createQuery("SELECT T FROM TCubeRole T");
        List<TCubeRolesState> listOfRoles = new ArrayList<TCubeRolesState>();
        for (TCubeRole tp : (List<TCubeRole>) q.getResultList()) {
            TCubeRolesState trs = new TCubeRolesState(tp.getId(), tp.getName(), tp.getDescription(), tp.getCreatedDate());
            listOfRoles.add(trs);
        }
        return listOfRoles;
    }
    
    @Override
    public int registerPartner(String name, String bizAddress1, String bizAddress2, String bizPostalCode, String billingAddress1, String billingAddress2, String billingPostalCode, String countryCode, String phone, String country, String province, String city, String bizEmail, int sector, long motherCorpID, String managerUsername) {
        //Get reference to the manager's account
        PartnerAccount pa = em.find(PartnerAccount.class, managerUsername);
        Partner p = new Partner();
        if (motherCorpID == 0) p.createPartner(name, bizAddress1, bizAddress2, bizPostalCode, billingAddress1, billingAddress2, billingPostalCode, countryCode, phone, country, province, city, bizEmail, sector, false, pa);
        else {
            p.createPartner(name, bizAddress1, bizAddress2, bizPostalCode, billingAddress1, billingAddress2, billingPostalCode, countryCode, phone, country, province, city, bizEmail, sector, true, pa);
            //Add this partner to the listOfBranches of the mother corp
            Partner motherCorp = em.find(Partner.class, motherCorpID);
            List<Partner> listOfBranches = motherCorp.getListOfBranches();
            listOfBranches.add(p);
            motherCorp.setListOfBranches(listOfBranches);
        }
        pa.setPartner(p);
        em.persist(p);
        return Constants.STATUS_SUCCESSFUL;
    }
    
    @Override
    public int createTCubeRole(int id, String name, String description, int[] listOfPrivileges) {
        System.err.println("HERE");
        Query q = em.createQuery("SELECT T FROM TCubeRole T WHERE T.name=:name");
        q.setParameter("name", name);
        if (!q.getResultList().isEmpty()) return Constants.STATUS_NOT_UNIQUE;
        else {
            TCubeRole tr = new TCubeRole();
            tr.createTCubeRole(id, name, description);
            for (int privilegeID : listOfPrivileges) {
                TCubePrivilege tp = em.find(TCubePrivilege.class, privilegeID);
                if (tp == null) return Constants.STATUS_NOT_FOUND;
                tr.addPrivilege(tp);
            }
            em.persist(tr);
            System.err.println("HERE");
            return Constants.STATUS_SUCCESSFUL;
        }
    }
    
    @Override
    public int createTCubePrivilege(String name, String description) {
        Query q = em.createQuery("SELECT T FROM TCubePrivilege T WHERE T.name=:name");
        q.setParameter("name", name);
        if (!q.getResultList().isEmpty()) return Constants.STATUS_NOT_UNIQUE;
        else {
            TCubePrivilege tp = new TCubePrivilege();
            tp.createPrivilege(Constants.TCUBE_NUMBER_OF_PRIVILEGES++, name, description);
            em.persist(tp);
            return Constants.STATUS_SUCCESSFUL;
        }
    }
    
    @Override
    public int grantTCubePrivilege(String username, int[] listOfPrivileges) {
        TCubeAccount ta = em.find(TCubeAccount.class, username);
        if (ta == null) return Constants.STATUS_NOT_FOUND;
        else {
            List<TCubePrivilege> privileges = new ArrayList<TCubePrivilege>();
            for (int i : listOfPrivileges) {
                TCubePrivilege tp = em.find(TCubePrivilege.class, i);
                privileges.add(tp);
            }
            ta.setListOfPrivileges(privileges);
            return Constants.STATUS_SUCCESSFUL;
        }
    }
    
    //Private methods of TCube Bean:
    private String getUniqueID() {
        int NUM_CHARS = 20;
        String chars = "abcdefghijklmonpqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random r = new Random();
        char[] buf = new char[NUM_CHARS];

        for (int i = 0; i < buf.length; i++) {
            buf[i] = chars.charAt(r.nextInt(62));
        }

        return new String(buf);
    }
    
    private void sendEmail(int msgType, String recipient, String link, String... args) {
        try {
            Connection      queueConnection = queueConnectionFactory.createConnection();
            Session         queueSession    = queueConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageProducer queueProducer   = queueSession.createProducer(emailQueue);
            MapMessage      msg             = queueSession.createMapMessage();
            //Plug in all data available and send the message
            msg.setInt("msgType", msgType);
            msg.setString("recipient", recipient);
            if (link != null) msg.setString("link", link);
            int i = 1;
            for (String str : args) {
                msg.setString("str"+i, str);
                i++;
            }
            queueProducer.send(msg);
            queueConnection.close();
        } catch (JMSException ex) {
            Logger.getLogger(CustomerBean.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    @Override
    public int activateCubes() {
        Query q = em.createQuery("SELECT C FROM Cubes C");
        for (Cubes c : (List<Cubes>) q.getResultList()) {
            if (c.getSequence() > Constants.CURRENT_SEQUENCE_NUMBER && c.getSequence() <= Constants.CURRENT_SEQUENCE_NUMBER + Constants.NUMBER_OF_CUBES_TO_BE_DISPLAYED_EACH_DAY) {
                c.setActive(true);
            }
        }
        return Constants.STATUS_SUCCESSFUL;
    }
    
    @Override
    public int deactivateCubes() {
        Query q = em.createQuery("SELECT C FROM Cubes C");
        for (Cubes c : (List<Cubes>) q.getResultList()) {
            if (c.getSequence() <= Constants.CURRENT_SEQUENCE_NUMBER && c.getSequence() > Constants.CURRENT_SEQUENCE_NUMBER - Constants.NUMBER_OF_CUBES_TO_BE_DISPLAYED_EACH_DAY) {
                c.setActive(false);
            }
        }
        return Constants.STATUS_SUCCESSFUL;
    }
}
